home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / viewkit / VCal / VCal.c++ < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  54.5 KB  |  2,231 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. #include <stdio.h>
  18. #include <unistd.h>
  19. #include <libc.h>
  20. #include <time.h>
  21. #include <sys/time.h>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <pwd.h>
  25. #include "VCal.h"
  26. #include "Utils.h"
  27. #include "DayView.h"
  28. #include "MonthOverview.h"
  29. #include "WeekOverview.h"
  30. #include "Info.h"
  31. #include "Preferences.h"
  32. #include "PrefDialog.h"
  33. #include "PrintPS.h"
  34. #include "path.h"
  35. #include <Vk/VkFormat.h>
  36. #include <Vk/VkHelp.h>
  37. #include <Vk/VkHelpPane.h>
  38. #include <Vk/VkFileSelectionDialog.h>
  39. #include <Vk/VkResource.h>
  40. #include <Vk/VkRepeatButton.h>
  41. #include <Vk/VkPopupMenu.h>
  42. #include <Vk/VkSubMenu.h>
  43. #include <Vk/VkSimpleWindow.h>
  44. #include <Vk/VkMenuBar.h>
  45. #include <Vk/VkHelpPane.h>
  46. #include <Vk/VkWarningDialog.h>
  47. #include <Vk/VkQuestionDialog.h>
  48. #include <Vk/VkFatalErrorDialog.h>
  49. #include <Vk/VkChildrenHandler.h>
  50. #include <Vk/VkPrefItem.h>
  51.  
  52. #include <Xm/Form.h>
  53. #include <Xm/RowColumn.h>
  54. #include <Xm/PushB.h>
  55. #include <Xm/PushBG.h>
  56. #include <Xm/Label.h>
  57. #include <Xm/LabelG.h>
  58. #include <Xm/SeparatoG.h>
  59. #include <Xm/MessageB.h>
  60. #include <Xm/FileSB.h>
  61.  
  62. static Arg args[10];
  63. static int count;
  64.  
  65. #define TILE_DISTANCE    5
  66.  
  67. void
  68. VCal::load_menu(Widget, XtPointer client_data, XtPointer)
  69. {
  70.   VCal *obj = (VCal *) client_data;
  71.   
  72.   obj->loadMenu();
  73. }
  74.  
  75. void
  76. VCal::save_menu(Widget, XtPointer client_data, XtPointer)
  77. {
  78.   VCal *obj = (VCal *) client_data;
  79.   
  80.   obj->writeFile();
  81. }
  82.  
  83. Boolean
  84. VCal::write_file(XtPointer client_data)
  85. {
  86.   VCal *obj = (VCal *) client_data;
  87.   
  88.   XmUpdateDisplay(obj->baseWidget());
  89.   obj->writeFile();
  90.   obj->writeID = NULL;
  91.   return True;
  92. }
  93.  
  94. void
  95. VCal::saveas_menu(Widget, XtPointer client_data, XtPointer)
  96. {
  97.   VCal *obj = (VCal *) client_data;
  98.   
  99.   obj->saveAsMenu();
  100. }
  101.  
  102. void
  103. VCal::save_text(Widget, XtPointer client_data, XtPointer)
  104. {
  105.   VCal *obj = (VCal *) client_data;
  106.   
  107.   if (obj->dayText && obj->dayText->getValue()) {
  108.     obj->saveAsTextMenu(False, obj, obj->nowDay, obj->nowMonth, obj->nowYear);
  109.   } else {
  110.     obj->saveAsTextMenu(False, obj, obj->nowDay, obj->month, obj->year);
  111.   }
  112. }
  113.  
  114. void
  115. VCal::display_overview(Widget, XtPointer client_data, XtPointer)
  116. {
  117.   VCal *obj = (VCal *) client_data;
  118.   
  119.   obj->displayMonthOverview();
  120. }
  121.  
  122. void
  123. VCal::week_overview(Widget, XtPointer client_data, XtPointer)
  124. {
  125.   VCal *obj = (VCal *) client_data;
  126.   
  127.   obj->displayWeekOverview();
  128. }
  129.  
  130. void
  131. VCal::month_print(Widget, XtPointer client_data, XtPointer)
  132. {
  133.   VCal *obj = (VCal *) client_data;
  134.   
  135.   obj->monthPrint();
  136. }
  137.  
  138. void
  139. VCal::savetext_ok(Widget, XtPointer client_data, XtPointer)
  140. {
  141.   VCal *obj = (VCal *) client_data;
  142.   
  143.   obj->saveAsTextOK();
  144.   obj->saveAsTextCancel();
  145. }
  146.  
  147. void
  148. VCal::savetext_cancel(Widget, XtPointer client_data, XtPointer)
  149. {
  150.   VCal *obj = (VCal *) client_data;
  151.   
  152.   obj->saveAsTextCancel();
  153. }
  154.  
  155. void
  156. VCal::pref_menu(Widget, XtPointer client_data, XtPointer)
  157. {
  158.   VCal *obj = (VCal *) client_data;
  159.   
  160.   obj->prefMenu();
  161. }
  162.  
  163. void
  164. VCal::factory_menu(Widget, XtPointer client_data, XtPointer)
  165. {
  166.   VCal *obj = (VCal *) client_data;
  167.   
  168.   obj->factoryMenu();
  169. }
  170.  
  171. void
  172. VCal::quit_menu(Widget, XtPointer, XtPointer)
  173. {
  174.   theApplication->quitYourself();
  175. }
  176.  
  177. void
  178. VCal::save_quit(Widget, XtPointer client_data, XtPointer)
  179. {
  180.   VCal *obj = (VCal *) client_data;
  181.   
  182.   if (obj->writeFile()) {
  183.     delete obj;
  184.   }
  185. }
  186.  
  187. void
  188. VCal::timeout_proc(XtPointer client_data, XtIntervalId *)
  189. {
  190.   VCal *obj = (VCal *) client_data;
  191.   
  192.   obj->handleTimeOut();
  193. }
  194.  
  195. void
  196. VCal::snooze_proc(XtPointer client_data, XtIntervalId *id)
  197. {
  198.   VCal *obj = (VCal *) client_data;
  199.   
  200.   obj->handleSnooze(*id);
  201. }
  202.  
  203. void
  204. VCal::month_select(Widget w, XtPointer client_data, XEvent *event)
  205. {
  206.   VCal *obj = (VCal *) client_data;
  207.   
  208.   if (event->xbutton.button == 1 && w == obj->monthHeader) {
  209.     obj->displayToday();
  210.   } else if (event->xbutton.button == 3) {
  211.     obj->displayActionsPopup(&event->xbutton);
  212.   }
  213. }
  214.  
  215. void
  216. VCal::day_select(Widget w, XtPointer client_data, XtPointer call_data)
  217. {
  218.   VCal *obj = (VCal *) client_data;
  219.   XmPushButtonCallbackStruct *cb = (XmPushButtonCallbackStruct *) call_data;
  220.   int day;
  221.   
  222.   count = 0;
  223.   XtSetArg(args[count], XmNuserData, &day);  count++;
  224.   XtGetValues(w, args, count);
  225.   if (cb->event && cb->event->type == ButtonRelease &&
  226.       (cb->event->xbutton.state & ShiftMask)) {
  227.     obj->weekSelect(day);
  228.   } else {
  229.     obj->daySelect(day);
  230.   }
  231. }
  232.  
  233. void
  234. VCal::month_popup(Widget, XtPointer client_data, XtPointer)
  235. {
  236.   VkCallbackStruct *cb = (VkCallbackStruct *) client_data;
  237.   VCal *obj = (VCal *) cb->obj;
  238.   int month = (int) cb->client_data;
  239.   
  240.   obj->selectMonth(month);
  241. }
  242.  
  243. void
  244. VCal::resize_day(Widget, XtPointer client_data, XEvent *event, Boolean *)
  245. {
  246.   VCal *obj = (VCal *) client_data;
  247.   
  248.   if (event->type == ConfigureNotify) {
  249.     obj->resizeDay();
  250.   }
  251. }
  252.  
  253. Boolean
  254. VCal::check_file(XtPointer client_data)
  255. {
  256.   VCal *obj = (VCal *) client_data;
  257.   
  258.   obj->checkFileStat();
  259.   return True;
  260. }
  261.  
  262. Boolean
  263. VCal::dialog_delete(XtPointer client_data)
  264. {
  265.   VkQuestionDialog *dialog = (VkQuestionDialog *) client_data;
  266.   
  267.   delete dialog;
  268.   return True;
  269. }
  270.  
  271. void
  272. VCal::alarm_dismiss(Widget, XtPointer client_data, XtPointer)
  273. {
  274.   EntryAlarmInfo *info = (EntryAlarmInfo *) client_data;
  275.   VCal *obj = (VCal *) info->data;
  276.   
  277.   obj->alarmDismiss(info);
  278. }
  279.  
  280. void
  281. VCal::alarm_delete(Widget, XtPointer client_data, XtPointer)
  282. {
  283.   EntryAlarmInfo *info = (EntryAlarmInfo *) client_data;
  284.   VCal *obj = (VCal *) info->data;
  285.   
  286.   obj->alarmDelete(info);
  287. }
  288.  
  289. void
  290. VCal::alarm_snooze(Widget, XtPointer client_data, XtPointer)
  291. {
  292.   EntryAlarmInfo *info = (EntryAlarmInfo *) client_data;
  293.   VCal *obj = (VCal *) info->data;
  294.   
  295.   obj->alarmSnooze(info);
  296. }
  297.  
  298. /**********************************************************************/
  299.  
  300. void VCal::afterRealizeHook()
  301. {
  302.   int x;
  303.   GC tempGC;
  304.  
  305.   tile = XCreatePixmap(XtDisplay(_baseWidget),
  306.                XtWindow(_baseWidget),
  307.                TILE_DISTANCE, TILE_DISTANCE, 1);
  308.   tempGC = XCreateGC(XtDisplay(_baseWidget), tile, 0, NULL);
  309.   XSetForeground(XtDisplay(_baseWidget), tempGC, 0);
  310.   XFillRectangle(XtDisplay(_baseWidget), tile, tempGC, 0, 0,
  311.          TILE_DISTANCE, TILE_DISTANCE);
  312.   XSetForeground(XtDisplay(_baseWidget), tempGC, 1);
  313.   XDrawPoint(XtDisplay(_baseWidget), tile, tempGC, 0, 0);
  314.   for (x=1; x<TILE_DISTANCE; x++) {
  315.     XDrawPoint(XtDisplay(_baseWidget), tile, tempGC, x, TILE_DISTANCE-x);
  316.   }
  317.   XFreeGC(XtDisplay(_baseWidget), tempGC);
  318.  
  319.   XRaiseWindow(XtDisplay(clockLabel), XtWindow(clockLabel));
  320.   resizeDay();
  321. }
  322.  
  323. void
  324. VCal::deleteDayView(DayView *obj)
  325. {
  326.   if (obj && obj == dayView) {
  327.     dayView->hide();
  328.     if (weekOverview) {
  329.       weekOverview->updateDisplay();
  330.     }
  331.   }
  332. }
  333.  
  334. void
  335. VCal::dayViewChanged(DayView *obj, Entry *entry, Entry *oldEntry)
  336. {
  337.   Pixel now, back1, arm1, top1, bot1, back2, arm2, top2, bot2;
  338.   
  339.   dirty = True;
  340.   if ((!entry || !entry->repeating()) &&
  341.       (!oldEntry || !oldEntry->repeating()) &&
  342.       obj->month() == month && obj->year() == year) {
  343.     count = 0;
  344.     XtSetArg(args[count], XmNbackground, &back1);  count++;
  345.     XtSetArg(args[count], XmNarmColor, &arm1);  count++;
  346.     XtSetArg(args[count], XmNtopShadowColor, &top1);  count++;
  347.     XtSetArg(args[count], XmNbottomShadowColor, &bot1);  count++;
  348.     XtGetValues(pref->annotateMonth() ? annotateButton : normalButton,
  349.         args, count);
  350.     count = 0;
  351.     XtSetArg(args[count], XmNbackground, &back2);  count++;
  352.     XtSetArg(args[count], XmNarmColor, &arm2);  count++;
  353.     XtSetArg(args[count], XmNtopShadowColor, &top2);  count++;
  354.     XtSetArg(args[count], XmNbottomShadowColor, &bot2);  count++;
  355.     XtGetValues(normalButton, args, count);
  356.     
  357.     count = 0;
  358.     XtSetArg(args[count], XmNbackground, &now);  count++;
  359.     XtGetValues(dayWidgets[obj->day()+dayOffset-1], args, count);
  360.     if (now != obj->info()->annotate() ? back1 : back2) {
  361.       count = 0;
  362.       XtSetArg(args[count], XmNbackground,
  363.            obj->info()->annotate() ? back1 : back2);  count++;
  364.       XtSetArg(args[count], XmNarmColor,
  365.            obj->info()->annotate() ? arm1 : arm2);  count++;
  366.       XtSetArg(args[count], XmNtopShadowColor,
  367.            obj->info()->annotate() ? top1 : top2);  count++;
  368.       XtSetArg(args[count], XmNbottomShadowColor,
  369.            obj->info()->annotate() ? bot1 : bot2);  count++;
  370.       XtSetValues(dayWidgets[obj->day()+dayOffset-1], args, count);
  371.     }
  372.   } else if ((entry && entry->repeating()) ||
  373.          (oldEntry && oldEntry->repeating())) {
  374.     // Many date annotations could have changed, not just today
  375.     if (displayingCurrent) {
  376.       displayToday();
  377.     } else {
  378.       setDisplayDate(0, month, year);
  379.     }
  380.   }
  381.   if (dirty && pref->saveChangesAutomatically()) {
  382.     scheduleWriteFile();
  383.   }
  384.   today = findDateInfo(nowDay, nowMonth, nowYear);
  385.   if (monthOverview) {
  386.     monthOverview->updateDisplay();
  387.   }
  388.   if (weekOverview) {
  389.     weekOverview->updateDisplay();
  390.   }
  391. }
  392.  
  393. void
  394. VCal::addRepeatingEntry(RepeatingEntry *rentry)
  395. {
  396.   rentry->setNext(repeatingEntries->next());
  397.   repeatingEntries->setNext(rentry);
  398.   numRepeats++;
  399. }
  400.  
  401. void
  402. VCal::removeRepeatingEntry(RepeatingEntry *rentry)
  403. {
  404.   RepeatingEntry *r;
  405.   
  406.   r = repeatingEntries;
  407.   while (r->next()) {
  408.     if (r->next() == rentry) {
  409.       r->setNext(rentry->next());
  410.       rentry->setNext(NULL);
  411.       numRepeats--;
  412.       break;
  413.     } else {
  414.       r = r->next();
  415.     }
  416.   }
  417. }
  418.  
  419. void
  420. VCal::selectDay(int day, int month, int year)
  421. {
  422.   if (day < 1 || day > NumberOfDays(month, year)) {
  423.     fprintf(stderr, "Impossible, day selected is %d\n", day);
  424.   } else {
  425.     theApplication->busy();
  426.     
  427.     if (!dayView) {
  428.       dayView = new DayView(this, "dayView");
  429.     }
  430.     dayView->open();
  431.     dayView->raise();
  432.     dayView->show();
  433.     changeDayView(day, month, year);
  434.     if (weekOverview) {
  435.       weekOverview->selectWeek(day, month, year);
  436.     }
  437.     theApplication->notBusy();
  438.   }
  439. }
  440.  
  441. void
  442. VCal::prefChanged()
  443. {
  444.   theApplication->busy();
  445.   pref->savePreferences();
  446.   if (dayView) {
  447.     delete dayView;
  448.     dayView = NULL;
  449.   }
  450.   if (pref->saveChangesAutomatically()) {
  451.     writeFile();
  452.   }
  453.   updateDisplay(False);
  454.   updateTheIconName(NULL);
  455.   theApplication->notBusy();
  456. }
  457.  
  458. void
  459. VCal::saveAsTextMenu(Boolean todayDefault, VkSimpleWindow *parent,
  460.              int day, int month, int year)
  461. {
  462.   Widget filesb;
  463.  
  464.   if (!radioText) {
  465.     saveDay = day;
  466.     saveMonth = month;
  467.     saveYear = year;
  468.     radioText = new VkPrefRadio("saveAsTextToggles", True, True);
  469.     dayText = new VkPrefToggle("saveAsTextDay");
  470.     weekText = new VkPrefToggle("saveAsTextWeek");
  471.     monthText = new VkPrefToggle("saveAsTextMonth");
  472.     if (todayDefault) {
  473.       dayText->setValue(True);
  474.     } else {
  475.       monthText->setValue(True);
  476.     }
  477.     radioText->addItem(dayText);
  478.     radioText->addItem(weekText);
  479.     radioText->addItem(monthText);
  480.     theFileSelectionDialog->setTitle("Save As Text");
  481.     filesb =
  482.       theFileSelectionDialog->post(NULL,
  483.                    savetext_ok,
  484.                    savetext_cancel,
  485.                    (XtPointer) this,
  486.                    (char *) VkFormat("%s.%s", className(),
  487.                              "saveAsTextDialog"),
  488.                    parent);
  489.     radioText->build(filesb);
  490.     radioText->show();
  491.   }
  492. }
  493.  
  494. MemoryInfo *
  495. VCal::findDateInfo(int day, int month, int year)
  496. {
  497.   MemoryInfo *info;
  498.   
  499.   info = dayInfo;
  500.   while (info->next()) {
  501.     if (info->next()->sameDate(day, month, year)) {
  502.       return info->next();
  503.     } else {
  504.       info = info->next();
  505.     }
  506.   }
  507.   return NULL;
  508. }
  509.  
  510. void
  511. VCal::getToday(int *day, int *month, int *year)
  512. {
  513.   *day = nowDay;
  514.   *month = nowMonth;
  515.   *year = nowYear;
  516. }
  517.  
  518. void
  519. VCal::displayWeekOverview()
  520. {
  521.   if (dayView && dayView->visible()) {
  522.     displayWeekOverview(dayView->day(), dayView->month(), dayView->year());
  523.   } else {
  524.     displayWeekOverview(nowDay, nowMonth, nowYear);
  525.   }
  526. }
  527.  
  528. void
  529. VCal::emitDay(FILE *fd, int day, int month, int year)
  530. {
  531.   emitSingleDay(fd, day, month, year, True, True, "VCal: ");
  532. }
  533.  
  534. void
  535. VCal::emitWeek(FILE *fd, int day, int month, int year)
  536. {
  537.   int weekday, each, d, m, y;
  538.   char str[MAXSTR];
  539.  
  540. // This should be a routine in Utils
  541.   weekday = computeWeekday(day, month, year);
  542.   decrementDate(day, month, year, weekday-1, &d, &m, &y);
  543.   day = d;
  544.   month = m;
  545.   year = y;
  546.  
  547.   formatDate(day, month, year, str);
  548.   fprintf(fd, "VCal: Week of %s\n\n", str);
  549.   for (each=0; each<DAYS_IN_WEEK; each++) {
  550.     if (emitSingleDay(fd, day, month, year, False, False, NULL)) {
  551.       fprintf(fd, "\n");
  552.     }
  553.     augmentDate(day, month, year, 1, &d, &m, &y);
  554.     day = d;
  555.     month = m;
  556.     year = y;
  557.   }
  558. }
  559.  
  560. void
  561. VCal::emitMonth(FILE *fd, int, int month, int year)
  562. {
  563.   int days, each;
  564.   char str[MAXSTR];
  565.  
  566.   formatDate(month, year, str);
  567.   fprintf(fd, "VCal: Month of %s\n\n", str);
  568.   days = NumberOfDays(month, year);
  569.   for (each=1; each<=days; each++) {
  570.     if (emitSingleDay(fd, each, month, year, False, False, NULL)) {
  571.       fprintf(fd, "\n");
  572.     }
  573.   }
  574. }
  575.  
  576. void
  577. VCal::initCollect()
  578. {
  579.   numCollect = 0;
  580.   if (entries) {
  581.     free(entries);
  582.     entries = NULL;
  583.   }
  584. }
  585.  
  586. void
  587. VCal::collectSingleEntry(Entry *entry)
  588. {
  589.   if (entries) {
  590.     entries = (Entry **) realloc(entries, (numCollect+1)*sizeof(Entry *));
  591.   } else {
  592.     entries = (Entry **) malloc(sizeof(Entry *));
  593.   }
  594.   entries[numCollect] = entry;
  595.   numCollect++;
  596. }
  597.  
  598. int
  599. VCal::collectedCount()
  600. {
  601.   return numCollect;
  602. }
  603.  
  604. void
  605. VCal::sortCollection()
  606. {
  607.   Entry *tmp;
  608.   int each, each2;
  609.  
  610.   for (each=0; each<numCollect-1; each++) {
  611.     for (each2=each+1; each2<numCollect; each2++) {
  612.       if (entries[each]->start() > entries[each2]->start() ||
  613.       (entries[each]->start() == entries[each2]->start() &&
  614.        entries[each]->length() >
  615.        entries[each2]->length())) {
  616.     tmp = entries[each];
  617.     entries[each] = entries[each2];
  618.     entries[each2] = tmp;
  619.       }
  620.     }
  621.   }
  622. }
  623.  
  624. PrintPS *
  625. VCal::getPrint()
  626. {
  627.   if (!print) {
  628.     print = new PrintPS(this);
  629.   }
  630.   return print;
  631. }
  632.  
  633. /**********************************************************************/
  634.  
  635. void
  636. VCal::updateDisplay(Boolean read)
  637. {
  638.   int oldDay;
  639.   struct tm *now;
  640.   struct timeval t;
  641.   
  642.   theApplication->busy();
  643.   if (dayView) {
  644.     oldDay = dayView->day();
  645.     dayView->unselect();
  646.   }
  647.   // What was this for?  It screws up the today outline if I delete a timeout
  648.   // which is displayed right away on startup...
  649.   //  nowMonth = nowYear = nowDay = 0;
  650.   if (read) {
  651.     readFile();
  652.     today = findDateInfo(nowDay, nowMonth, nowYear);
  653.     gettimeofday(&t, NULL);
  654.     now = localtime(&t.tv_sec);
  655.     markTodayFired(now, False);
  656.   }
  657.   if (displayingCurrent) {
  658.     displayToday();
  659.   } else {
  660.     setDisplayDate(0, month, year);
  661.   }
  662.   if (dayView && oldDay) {
  663.     selectDay(oldDay, month, year);
  664.   }
  665.   intervalUpdate();
  666.   theApplication->notBusy();
  667. }
  668.  
  669. Widget
  670. VCal::setUpInterface(Widget parent)
  671. {
  672.   Widget separator;
  673.   
  674.   pref = new Preferences(parent);
  675.   showLates = pref->showLates();
  676.   
  677.   // Do this first, so that annotation of days works
  678.   readFile();
  679.   
  680.   if (!pref->hideMenuBar()) {
  681.     setMenuBar(new VkMenuBar());
  682.     if (menu()->helpPane()) {
  683.       menu()->helpPane()->setItemSensitivities(False, True, False, False,
  684.                            False);
  685.     }
  686.     fileMenu(menu());
  687.     displayMenu(menu());
  688.   }
  689.   actionsPopup = new VkPopupMenu();
  690.   actionsPopup->addLabel("vcalPopup");
  691.   actionsPopup->addSeparator();
  692.   fileMenu(actionsPopup);
  693.   displayMenu(actionsPopup);
  694.   helpMenu(actionsPopup);
  695.   actionsPopup->build(parent);
  696.   count = 0;
  697.   XtSetArg(args[count], XmNpopupEnabled, False);  count++;
  698.   XtSetValues(actionsPopup->baseWidget(), args, count);
  699.   
  700.   count = 0;
  701.   form = XmCreateForm(parent, "vcalForm", args, count);
  702.   XtAddEventHandler(form, ButtonPressMask, False,
  703.             (XtEventHandler) VCal::month_select,
  704.             (XtPointer) this);
  705.   
  706.   count = 0;
  707.   XtSetArg(args[count], XmNtopAttachment, XmATTACH_FORM);  count++;
  708.   XtSetArg(args[count], XmNbottomAttachment, XmATTACH_FORM);  count++;
  709.   XtSetArg(args[count], XmNleftAttachment, XmATTACH_FORM);  count++;
  710.   XtSetArg(args[count], XmNrightAttachment, XmATTACH_FORM);  count++;
  711.   hidden = XmCreateForm(form, "hidden", args, count);
  712.   XtManageChild(hidden);
  713.   
  714.   monthPrev = new VkRepeatButton("monthPrev", form, RB_arrowButtonGadget);
  715.   
  716.   count = 0;
  717.   XtSetArg(args[count], XmNtopAttachment, XmATTACH_FORM);  count++;
  718.   XtSetArg(args[count], XmNleftAttachment, XmATTACH_POSITION);  count++;
  719.   XtSetValues(monthPrev->baseWidget(), args, count);
  720.   monthPrev->show();
  721.   VkAddCallbackMethod(VkRepeatButton::buttonCallback, monthPrev, this,
  722.               VCal::doMonthPrev, NULL);
  723.   
  724.   monthNext = new VkRepeatButton("monthNext", form, RB_arrowButtonGadget);
  725.   
  726.   count = 0;
  727.   XtSetArg(args[count], XmNtopAttachment, XmATTACH_FORM);  count++;
  728.   XtSetArg(args[count], XmNrightAttachment, XmATTACH_POSITION);  count++;
  729.   XtSetValues(monthNext->baseWidget(), args, count);
  730.   monthNext->show();
  731.   VkAddCallbackMethod(VkRepeatButton::buttonCallback, monthNext, this,
  732.               VCal::doMonthNext, NULL);
  733.   
  734.   count = 0;
  735.   XtSetArg(args[count], XmNtopAttachment, XmATTACH_FORM);  count++;
  736.   XtSetArg(args[count], XmNleftAttachment, XmATTACH_WIDGET);  count++;
  737.   XtSetArg(args[count], XmNleftWidget, monthPrev->baseWidget());  count++;
  738.   XtSetArg(args[count], XmNrightAttachment, XmATTACH_WIDGET);  count++;
  739.   XtSetArg(args[count], XmNrightWidget, monthNext->baseWidget());  count++;
  740.   XtSetArg(args[count], XmNbottomAttachment,
  741.        XmATTACH_OPPOSITE_WIDGET);  count++;
  742.   XtSetArg(args[count], XmNbottomWidget, monthPrev->baseWidget());  count++;
  743.   monthHeader = XmCreateLabel(form, "monthHeader", args, count);
  744.   XtManageChild(monthHeader);
  745.   XtAddEventHandler(monthHeader, ButtonPressMask, False,
  746.             (XtEventHandler) VCal::month_select,
  747.             (XtPointer) this);
  748.   count = 0;
  749.   XtSetArg(args[count], XmNfontList, &labelFont);  count++;
  750.   XtGetValues(monthHeader, args, count);
  751.   
  752.   count = 0;
  753.   XtSetArg(args[count], XmNtopAttachment, XmATTACH_WIDGET);  count++;
  754.   XtSetArg(args[count], XmNtopWidget, monthHeader);  count++;
  755.   XtSetArg(args[count], XmNleftAttachment, XmATTACH_FORM);  count++;
  756.   XtSetArg(args[count], XmNrightAttachment, XmATTACH_FORM);  count++;
  757.   separator = XmCreateSeparatorGadget(form, "daySeparator", args, count);
  758.   XtManageChild(separator);
  759.   
  760.   createDayArea(form);
  761.   count = 0;
  762.   XtSetArg(args[count], XmNtopAttachment, XmATTACH_WIDGET);  count++;
  763.   XtSetArg(args[count], XmNtopWidget, separator);  count++;
  764.   XtSetArg(args[count], XmNleftAttachment, XmATTACH_POSITION);  count++;
  765.   XtSetArg(args[count], XmNrightAttachment, XmATTACH_POSITION);  count++;
  766.   XtSetArg(args[count], XmNbottomAttachment, XmATTACH_POSITION);  count++;
  767.   XtSetValues(dayArea, args, count);
  768.   XtAddEventHandler(dayArea, StructureNotifyMask, False,
  769.             &VCal::resize_day,
  770.             (XtPointer) this);
  771.   
  772.   count = 0;
  773.   XtSetArg(args[count], XmNrightAttachment,
  774.        XmATTACH_OPPOSITE_WIDGET);  count++;
  775.   XtSetArg(args[count], XmNrightWidget, dayArea);  count++;
  776.   XtSetArg(args[count], XmNbottomAttachment,
  777.        XmATTACH_OPPOSITE_WIDGET);  count++;
  778.   XtSetArg(args[count], XmNbottomWidget, dayArea);  count++;
  779.   // This has to be a label gadget vs. label widget cuz it overlaps dayArea
  780.   clockLabel = XmCreateLabel(form, "clockLabel", args, count);
  781.   XtManageChild(clockLabel);
  782.   
  783.   count = 0;
  784.   XtSetArg(args[count], XmNbackground, dayAreaBg);  count++;
  785.   normalButton = XmCreatePushButton(form, "normalButton", args, count);
  786.   count = 0;
  787.   // First try annotateColor, then sessionColor1, then sessionColor2,
  788.   // then sessionColor3, then readOnlyBackground, then default button background
  789.   if (VkGetResource(form, "annotateColor", "AnnotateColor")) {
  790.     annotateBg = (Pixel) VkGetResource(form, "annotateColor",
  791.                        "AnnotateColor", XmRPixel,
  792.                        "yellow");
  793.   } else if (VkGetResource(form, "sessionColor1", "SessionColor1")) {
  794.     annotateBg = (Pixel) VkGetResource(form, "sessionColor1",
  795.                        "SessionColor1", XmRPixel,
  796.                        "yellow");
  797.   } else if (VkGetResource(form, "sessionColor2", "SessionColor2")) {
  798.     annotateBg = (Pixel) VkGetResource(form, "sessionColor2",
  799.                        "SessionColor2", XmRPixel,
  800.                        "yellow");
  801.   } else if (VkGetResource(form, "sessionColor3", "SessionColor3")) {
  802.     annotateBg = (Pixel) VkGetResource(form, "sessionColor3",
  803.                        "SessionColor3", XmRPixel,
  804.                        "yellow");
  805.   } else if (VkGetResource(form, "readOnlyBackground", "ReadOnlyBackground")) {
  806.     annotateBg = (Pixel) VkGetResource(form, "readOnlyBackground",
  807.                        "ReadOnlyBackground", XmRPixel,
  808.                        "yellow");
  809.   } else if (VkGetResource(form, "selectColor", "SelectColor")) {
  810.     annotateBg = (Pixel) VkGetResource(form, "selectColor",
  811.                        "SelectColor", XmRPixel,
  812.                        "red");
  813.   } else {
  814.     annotateBg = dayAreaBg;
  815.   }
  816.   if (dayAreaBg != annotateBg) {
  817.     XtSetArg(args[count], XmNbackground, annotateBg);  count++;
  818.   }
  819.   annotateButton = XmCreatePushButton(form, "annotateButton", args, count);
  820.   
  821.   XtManageChild(form);
  822.   
  823.   handleTimeOut();
  824.   
  825.   return form;
  826. }
  827.  
  828. void
  829. VCal::handleWmDeleteMessage()
  830. {
  831.   theApplication->quitYourself();
  832. }
  833.  
  834. void
  835. VCal::fileMenu(VkMenu *parent)
  836. {
  837.   VkSubMenu *menu =   parent->addSubmenu("fileMenu");
  838.   
  839.   menu->addAction("prefMenuItem",
  840.           VCal::pref_menu,
  841.           (XtPointer) this);
  842.   menu->addAction("factoryMenuItem",
  843.           VCal::factory_menu,
  844.           (XtPointer) this);
  845.   menu->addSeparator();
  846.   menu->addAction("loadMenuItem",
  847.           VCal::load_menu,
  848.           (XtPointer) this);
  849.   menu->addAction("saveMenuItem",
  850.           VCal::save_menu,
  851.           (XtPointer) this);
  852.   menu->addAction("saveAsMenuItem",
  853.           VCal::saveas_menu,
  854.           (XtPointer) this);
  855.   menu->addAction("monthPrintMenuItem",
  856.           VCal::month_print,
  857.           (XtPointer) this);
  858.   menu->addSeparator();
  859.   menu->addAction("quitMenuItem",
  860.           VCal::quit_menu,
  861.           (XtPointer) this);
  862. }
  863.  
  864. void
  865. VCal::displayMenu(VkMenu *parent)
  866. {
  867.   VkSubMenu *menu =   parent->addSubmenu("displayMenu");
  868.   
  869.   monthMenu(menu);
  870.   menu->addAction("monthOverviewMenuItem",
  871.           VCal::display_overview,
  872.           (XtPointer) this);
  873.   menu->addAction("weekMonthOverviewMenuItem",
  874.           VCal::week_overview,
  875.           (XtPointer) this);
  876.   menu->addAction("saveAsTextMenuItem",
  877.           VCal::save_text,
  878.           (XtPointer) this);
  879. }
  880.  
  881. void
  882. VCal::monthMenu(VkMenu *parent)
  883. {
  884.   int each;
  885.   VkCallbackStruct *callbackStructs;
  886.   VkSubMenu *menu =   parent->addSubmenu("monthMenu");
  887.   
  888.   callbackStructs = new VkCallbackStruct[12];    // memory leak
  889.   for (each=0; each<12; each++) {
  890.     callbackStructs[each].obj = this;
  891.     callbackStructs[each].client_data = (void *) (each+1);
  892.     menu->addAction(monthString(each+1), VCal::month_popup,
  893.             (XtPointer) &callbackStructs[each]);
  894.   }
  895.   
  896. }
  897.  
  898. void
  899. VCal::helpMenu(VkMenu *parent)
  900. {
  901.   VkHelpPane *helpPane;
  902.   
  903.   helpPane = new VkHelpPane("helpMenu");
  904.   helpPane->setItemSensitivities(False, True, False, False, False);
  905.   parent->addSubmenu(helpPane);
  906. }
  907.  
  908. void
  909. VCal::createDayArea(Widget parent)
  910. {
  911.   count = 0;
  912.   dayArea = XmCreateRowColumn(parent, "dayArea", args, count);
  913.   
  914.   createDayWidgets(dayArea);
  915.   
  916.   XtManageChild(dayArea);
  917. }
  918.  
  919. void
  920. VCal::createDayWidgets(Widget parent)
  921. {
  922.   int each;
  923.   char str[16];
  924.   
  925.   count = 0;
  926.   XtSetArg(args[count], XmNbackground, &dayAreaBg);  count++;
  927.   XtSetArg(args[count], XmNforeground, &dayAreaFg);  count++;
  928.   XtSetArg(args[count], XmNbottomShadowColor, &nonHighlight);  count++;
  929.   XtGetValues(parent, args, count);
  930.  
  931.   count = 0;
  932.   XtSetArg(args[count], XmNborderColor, dayAreaBg);  count++;
  933.   for (each=0; each<DAYS_IN_WEEK; each++) {
  934.     sprintf(str, "dayHeader%d", each+1);
  935.     // These can't be gadgets because the border width has to be consistent
  936.     dayHeaders[each] = XmCreateLabel(parent, str, args, count);
  937.   }
  938.   XtManageChildren(dayHeaders, DAYS_IN_WEEK);
  939.   for (each=0; each<MAX_DAYS; each++) {
  940.     dayWidgets[each] = XmCreatePushButton(parent, "dayWidget", args, count);
  941.     XtAddCallback(dayWidgets[each], XmNactivateCallback,
  942.           VCal::day_select,
  943.           (XtPointer) this);
  944.   }
  945.   XtManageChildren(dayWidgets, MAX_DAYS);
  946.   count = 0;
  947.   XtSetArg(args[count], XmNborderWidth, &buttonBorder);  count++;
  948.   XtGetValues(dayWidgets[0], args, count);
  949. }
  950.  
  951. void
  952. VCal::setDisplayDate(int d, int m, int y)
  953. {
  954.   int first;
  955.   
  956.   theApplication->busy();
  957.   month = m;
  958.   year = y;
  959.   first = FirstDay(month, year);
  960.   displayHeader(d, month, year);
  961.   displayDays((first + DAYS_IN_WEEK - 1) % DAYS_IN_WEEK,
  962.           NumberOfDays(month, year));
  963.   annotateDays(NumberOfDays(month, year));
  964.   if (monthOverview) {
  965.     monthOverview->selectMonth(month, year);
  966.   }
  967.   theApplication->notBusy();
  968. }
  969.  
  970. void
  971. VCal::displayDays(int offset, int num)
  972. {
  973.   XmString xs;
  974.   char str[8];
  975.   int each, day;
  976.   Dimension shadow;
  977.   Boolean sensitive;
  978.   Pixel back2, arm2, top2, bot2, bg;
  979.   
  980.   count = 0;
  981.   XtSetArg(args[count], XmNbackground, &back2);  count++;
  982.   XtSetArg(args[count], XmNarmColor, &arm2);  count++;
  983.   XtSetArg(args[count], XmNtopShadowColor, &top2);  count++;
  984.   XtSetArg(args[count], XmNbottomShadowColor, &bot2);  count++;
  985.   XtGetValues(normalButton, args, count);
  986.   
  987.   dayOffset = offset;
  988.   for (each=0; each<MAX_DAYS; each++) {
  989.     if (each >= dayOffset && each < dayOffset+num) {
  990.       day = each-dayOffset+1;
  991.       sprintf(str, "%d", day);
  992.       xs = XmStringCreateSimple(str);
  993.       shadow = 1;
  994.       sensitive = True;
  995.       bg = back2;
  996.     } else {
  997.       day = 0;
  998.       xs = XmStringCreateSimple("");
  999.       shadow = 0;
  1000.       sensitive = False;
  1001.       bg = dayAreaBg;
  1002.     }
  1003.     count = 0;
  1004.     XtSetArg(args[count], XmNuserData, day);  count++;
  1005.     XtSetArg(args[count], XmNlabelString, xs);  count++;
  1006.     XtSetArg(args[count], XmNshadowThickness, shadow);  count++;
  1007.     XtSetArg(args[count], XmNsensitive, sensitive);  count++;
  1008.     XtSetArg(args[count], XmNbackground, bg);  count++;
  1009.     XtSetArg(args[count], XmNarmColor, arm2);  count++;
  1010.     XtSetArg(args[count], XmNtopShadowColor, top2);  count++;
  1011.     XtSetArg(args[count], XmNbottomShadowColor, bot2);  count++;
  1012.     if (displayingCurrent && (each-dayOffset+1) == nowDay) {
  1013.       XtSetArg(args[count], XmNborderColor, dayAreaFg);  count++;
  1014.     } else {
  1015.       XtSetArg(args[count], XmNborderColor, dayAreaBg);  count++;
  1016.     }
  1017.     XtSetValues(dayWidgets[each], args, count);
  1018.     XmStringFree(xs);
  1019.   }
  1020. }
  1021.  
  1022. void
  1023. VCal::displayHeader(int day, int month, int year)
  1024. {
  1025.   char str[256];
  1026.   XmString xs;
  1027.   
  1028.   if (displayingCurrent) {
  1029.     formatDate(day, month, year, str);
  1030.   } else {
  1031.     formatDate(month, year, str);
  1032.   }
  1033.   xs = XmStringCreateSimple(str);
  1034.   count = 0;
  1035.   XtSetArg(args[count], XmNlabelString, xs);  count++;
  1036.   XtSetValues(monthHeader, args, count);
  1037.   XmStringFree(xs);
  1038. }
  1039.  
  1040. void
  1041. VCal::handleTimeOut()
  1042. {
  1043.   struct timeval t;
  1044.   int excess, delay;
  1045.   
  1046.   intervalUpdate();
  1047.   gettimeofday(&t, NULL);
  1048.   excess = ((int) t.tv_sec) % pref->updateInterval();
  1049.   delay = pref->updateInterval()-excess;
  1050.   if (delay < 1) {
  1051.     delay = 1;
  1052.   }
  1053.   XtAppAddTimeOut(theApplication->appContext(), delay*1000,
  1054.           (XtTimerCallbackProc) VCal::timeout_proc,
  1055.           (XtPointer) this);
  1056. }
  1057.  
  1058. void
  1059. VCal::intervalUpdate()
  1060. {
  1061.   struct tm *now;
  1062.   struct timeval t;
  1063.   
  1064.   if (!updating) {
  1065.     updating = True;
  1066.     gettimeofday(&t, NULL);
  1067.     now = localtime(&t.tv_sec);
  1068.     updateClock(now);
  1069.     if (pref->updateIconName()) {
  1070.       updateTheIconName(now);
  1071.     } else if (pref->updateIconDate() != iconIsDate ||
  1072.            iconIsTime) {
  1073.       updateTheIconName(now);
  1074.     }
  1075.     checkDate(now);
  1076.     if (dayView) {
  1077.       dayView->updateTime();
  1078.     }
  1079.     checkAlarms(now->tm_hour*60+now->tm_min);
  1080.     checkFileStat();
  1081.     updating = False;
  1082.   }
  1083. }
  1084.  
  1085. void
  1086. VCal::checkDate(struct tm *now)
  1087. {
  1088.   if (now->tm_mday != nowDay ||
  1089.       now->tm_mon+1 != nowMonth ||
  1090.       now->tm_year+1900 != nowYear) {
  1091.     nowDay = now->tm_mday;
  1092.     nowMonth = now->tm_mon+1;
  1093.     nowYear = now->tm_year+1900;
  1094.     today = findDateInfo(nowDay, nowMonth, nowYear);
  1095.     
  1096.     if (displayingCurrent) {
  1097.       setDisplayDate(now->tm_mday, now->tm_mon+1, now->tm_year+1900);
  1098.     } else {
  1099.       setDisplayDate(now->tm_mday, month, year);
  1100.     }
  1101.     
  1102.     // Don't post alarms for those events that have already occured
  1103.     markTodayFired(now, showLates);
  1104.     
  1105.     if (pref->updateIconDate()) {
  1106.       updateTheIconName(now);
  1107.     }
  1108.     checkDayAdvanceAlarms();
  1109.   }
  1110.   showLates = False;    // Only show late appointments once
  1111. }
  1112.  
  1113. void
  1114. VCal::updateClock(struct tm *now)
  1115. {
  1116.   char str[256];
  1117.   XmString xs;
  1118.   
  1119.   formatTime(now->tm_hour, now->tm_min, pref->clock24(), str);
  1120.   xs = XmStringCreateSimple(str);
  1121.   count = 0;
  1122.   XtSetArg(args[count], XmNlabelString, xs);  count++;
  1123.   XtSetValues(clockLabel, args, count);
  1124.   XmStringFree(xs);
  1125. }
  1126.  
  1127. void
  1128. VCal::updateTheIconName(struct tm *now)
  1129. {
  1130.   struct timeval t;
  1131.   char str[256];
  1132.   char *name, *p;
  1133.   
  1134.   if (!now) {
  1135.     gettimeofday(&t, NULL);
  1136.     now = localtime(&t.tv_sec);
  1137.   }
  1138.   strcpy(str, "");
  1139.   iconIsTime = pref->updateIconName();
  1140.   iconIsDate = pref->updateIconDate();
  1141.   if (iconIsDate) {
  1142.     sprintf(str, "%d/%d", nowMonth, nowDay);
  1143.     if (iconIsTime) {
  1144.       strcat(str, ", ");
  1145.     } else {
  1146.       sprintf(str+strlen(str), "/%d", nowYear % 100);
  1147.     }
  1148.   }
  1149.   if (iconIsTime) {
  1150.     formatTime(now->tm_hour, now->tm_min, pref->clock24(), str+strlen(str));
  1151.   }
  1152.   if (!iconIsDate && !iconIsTime) {
  1153.     name = theApplication->argv(0);
  1154.     p = strrchr(name, '/');
  1155.     if (p) {
  1156.       p++;
  1157.     } else {
  1158.       p = name;
  1159.     }
  1160.     strcpy(str, p);
  1161.   }
  1162.   setIconName(str);
  1163. }
  1164.  
  1165. void
  1166. VCal::displayToday()
  1167. {
  1168.   struct tm *now;
  1169.   struct timeval t;
  1170.   
  1171.   displayingCurrent = True;
  1172.   gettimeofday(&t, NULL);
  1173.   now = localtime(&t.tv_sec);
  1174.   setDisplayDate(now->tm_mday, now->tm_mon+1, now->tm_year+1900);
  1175. }
  1176.  
  1177. void
  1178. VCal::doMonthPrev(Widget, XtPointer, XtPointer)
  1179. {
  1180.   if (month > 1) {
  1181.     month--;
  1182.   } else {
  1183.     month = 12;
  1184.     year--;
  1185.   }
  1186.   displayingCurrent = (month == nowMonth && year == nowYear);
  1187.   setDisplayDate(nowDay, month, year);
  1188. }
  1189.  
  1190. void
  1191. VCal::doMonthNext(Widget, XtPointer, XtPointer)
  1192. {
  1193.   if (month < 12) {
  1194.     month++;
  1195.   } else {
  1196.     month = 1;
  1197.     year++;
  1198.   }
  1199.   displayingCurrent = (month == nowMonth && year == nowYear);
  1200.   setDisplayDate(nowDay, month, year);
  1201. }
  1202.  
  1203. void
  1204. VCal::daySelect(int day)
  1205. {
  1206.   selectDay(day, month, year);
  1207.   if (day == nowDay && month == nowMonth && year == nowYear) {
  1208.     dayView->scrollToCurrentTime();
  1209.   } else {
  1210.     dayView->scrollToBeginning();
  1211.   }
  1212. }
  1213.  
  1214. void
  1215. VCal::weekSelect(int day)
  1216. {
  1217.   if (dayView) {
  1218.     changeDayView(day, month, year);
  1219.     if (day == nowDay && month == nowMonth && year == nowYear) {
  1220.       dayView->scrollToCurrentTime();
  1221.     } else {
  1222.       dayView->scrollToBeginning();
  1223.     }
  1224.   }
  1225.   displayWeekOverview(day, month, year);
  1226. }
  1227.  
  1228. void
  1229. VCal::annotateDays(int num)
  1230. {
  1231.   MemoryInfo *current;
  1232.   Pixel back1, arm1, top1, bot1;
  1233.   RepeatingEntry *rentry;
  1234.   int each;
  1235.   
  1236.   count = 0;
  1237.   XtSetArg(args[count], XmNbackground, &back1);  count++;
  1238.   XtSetArg(args[count], XmNarmColor, &arm1);  count++;
  1239.   XtSetArg(args[count], XmNtopShadowColor, &top1);  count++;
  1240.   XtSetArg(args[count], XmNbottomShadowColor, &bot1);  count++;
  1241.   XtGetValues(pref->annotateMonth() ? annotateButton : normalButton,
  1242.           args, count);
  1243.   
  1244.   count = 0;
  1245.   XtSetArg(args[count], XmNbackground, back1);  count++;
  1246.   XtSetArg(args[count], XmNarmColor, arm1);  count++;
  1247.   XtSetArg(args[count], XmNtopShadowColor, top1);  count++;
  1248.   XtSetArg(args[count], XmNbottomShadowColor, bot1);  count++;
  1249.   
  1250.   rentry = repeatingEntries;
  1251.   while (rentry->next()) {
  1252.     rentry = rentry->next();
  1253.     if (rentry->annotate()) {
  1254.       for (each=1; each<=num; each++) {
  1255.     if (rentry->repeatApplies(each, month, year)) {
  1256.       XtSetValues(dayWidgets[each+dayOffset-1], args, count);
  1257.     }
  1258.       }
  1259.     }
  1260.   }
  1261.   
  1262.   current = dayInfo;
  1263.   while (current->next()) {
  1264.     current = current->next();
  1265.     if (current->annotate() &&
  1266.     current->month() == month && current->year() == year) {
  1267.       XtSetValues(dayWidgets[current->day()+dayOffset-1], args, count);
  1268.     }
  1269.   }
  1270. }
  1271.  
  1272. Boolean
  1273. VCal::readFile()
  1274. {
  1275.   FILE *fd;
  1276.   int each, version, numd, numr;
  1277.   char str[MAXSTR], *fname;
  1278.   MemoryInfo *current, *last;
  1279.   RepeatingEntry *rentry;
  1280.   
  1281.   fd = fopen(pref->filename(), "r");
  1282.   if (fd) {
  1283.     if (!readInt(fd, &version)) {
  1284.       sprintf(str, "%s does not have the correct file format.\nVersion number could not be read.",
  1285.           pref->filename());
  1286.       theWarningDialog->post(str);
  1287.       return False;
  1288.       // Version 5 can read version 4 files
  1289.       //   alarm advance notice used to be min, now free-form text
  1290.     } else if (version > VERSION_NUMBER) {
  1291.       sprintf(str, "%s has version %d, not version %d.",
  1292.           pref->filename(), version, VERSION_NUMBER);
  1293.       theFatalErrorDialog->post(str);
  1294.       return False;
  1295.     } else if (!readInt(fd, &numd)) {
  1296.       sprintf(str, "%s does not have the correct file format.\nDay number could not be read.",
  1297.           pref->filename());
  1298.       theWarningDialog->post(str);
  1299.       return False;
  1300.     } else if (!readInt(fd, &numr)) {
  1301.       sprintf(str, "%s does not have the correct file format.\nRepeats number could not be read.",
  1302.           pref->filename());
  1303.       theWarningDialog->post(str);
  1304.       return False;
  1305.     } else {
  1306.       clearData();
  1307.       last = dayInfo;
  1308.       current = dayInfo;
  1309.       for (each=0; each<numd; each++) {
  1310.     if (!readDay(fd, current, version)) {
  1311.       sprintf(str, "%s does not have the correct file format.\nDay info was erroneous or truncated.\nThe last date read was %d/%d/%d",
  1312.           pref->filename(), last->month(), last->day(), last->year());
  1313.       theWarningDialog->post(str);
  1314.       break;
  1315.     } else {
  1316.       numDays++;
  1317.       last = current;
  1318.       current = current->next();
  1319.     }
  1320.       }
  1321.       for (each=0; each<numr; each++) {
  1322.     rentry = new RepeatingEntry();
  1323.     if (!rentry->readDate(fd, version) ||
  1324.         !rentry->readEntry(fd, version)) {
  1325.       delete rentry;
  1326.     } else {
  1327.       addRepeatingEntry(rentry);
  1328.     }
  1329.       }
  1330.     }
  1331.     fclose(fd);
  1332.     dirty = False;
  1333.   } else {
  1334.     fname = pathexpandtilde(DEFAULT_FILENAME);
  1335.     if (strcmp(pref->filename(), fname)) {
  1336.       sprintf(str, "Couldn't read %s.", pref->filename());
  1337.       theWarningDialog->post(str);
  1338.     }
  1339.     return False;
  1340.   }
  1341.   updateFileStat();
  1342.   return True;
  1343. }
  1344.  
  1345. Boolean
  1346. VCal::readDay(FILE *fd, MemoryInfo *parent, int version)
  1347. {
  1348.   MemoryInfo *info;
  1349.   
  1350.   info = new MemoryInfo();
  1351.   if (info->readDay(fd, version)) {
  1352.     parent->setNext(info);
  1353.     return True;
  1354.   } else {
  1355.     delete info;
  1356.     return False;
  1357.   }
  1358. }
  1359.  
  1360. void
  1361. VCal::scheduleWriteFile()
  1362. {
  1363.   if (!writeID) {
  1364.     writeID = XtAppAddWorkProc(XtWidgetToApplicationContext(_baseWidget),
  1365.                    VCal::write_file,
  1366.                    (XtPointer) this);
  1367.   }
  1368. }
  1369.  
  1370. Boolean
  1371. VCal::writeFile()
  1372. {
  1373.   FILE *fd;
  1374.   char str[256], tempFile[256], *dir;
  1375.   int each, adjust;
  1376.   MemoryInfo *current;
  1377.   RepeatingEntry *rentry;
  1378.   
  1379.   if (fileChanged()) {
  1380.     if (checkInhibited) {
  1381.       return False;
  1382.     }
  1383.     inhibitCheckFile();
  1384.     if (theQuestionDialog->postAndWait(
  1385.                        "Someone else has changed the database file.\nOverwrite their changes?")
  1386.     == VkDialogManager::CANCEL) {
  1387.       allowCheckFile();
  1388.       checkFileSoon();
  1389.       return False;
  1390.     }
  1391.     allowCheckFile();
  1392.   }
  1393.   
  1394.   dir = pathdirname(pref->filename());
  1395.   if (dir && strlen(dir)) {
  1396.     sprintf(tempFile, "%s/.vcal-XXXXXX", dir);
  1397.   } else {
  1398.     strcpy(tempFile, ".vcal-XXXXXX");
  1399.   }
  1400.   mktemp(tempFile);
  1401.   fd = fopen(tempFile, "w");
  1402. /* Don't do this, since it will clobber the .vcal file if the filesystem
  1403.    is full, there are too many open files, etc.
  1404.   if (!fd) {
  1405.     strcpy(tempFile, pref->filename());
  1406.     fd = fopen(tempFile, "w");
  1407.   }
  1408. */
  1409.   if (!fd) {
  1410.     sprintf(str, "Couldn't create or write %s.", pref->filename());
  1411.     theWarningDialog->post(str);
  1412.     return False;
  1413.   } else {
  1414.     writeInt(fd, VERSION_NUMBER,
  1415.          pref->annotateFile() ? "Version Number" : NULL);
  1416.     adjust = 0;
  1417.     current = dayInfo;
  1418.     for (each=0; each<numDays; each++) {
  1419.       current = current->next();
  1420.       if (!current->size()) {
  1421.     adjust++;
  1422.       }
  1423.     }
  1424.     writeInt(fd, numDays-adjust,
  1425.          pref->annotateFile() ? "Number of Days" : NULL);
  1426.     writeInt(fd, numRepeats,
  1427.          pref->annotateFile() ? "Number of Repeating Entries" : NULL);
  1428.     current = dayInfo;
  1429.     for (each=0; each<numDays; each++) {
  1430.       current = current->next();
  1431.       if (current->size()) {
  1432.     current->writeDay(fd, pref->annotateFile());
  1433.       }
  1434.     }
  1435.     rentry = repeatingEntries;
  1436.     for (each=0; each<numRepeats; each++) {
  1437.       rentry = rentry->next();
  1438.       rentry->writeDate(fd, pref->annotateFile());
  1439.       rentry->writeEntry(fd, pref->annotateFile());
  1440.     }
  1441.     fclose(fd);
  1442.     dirty = False;
  1443.     if (strcmp(tempFile, pref->filename())) {
  1444. // First, preserve the mode of the file
  1445.       updateFileStat();
  1446.       if (lastFileStat.st_mtime) {
  1447.     chmod(tempFile, lastFileStat.st_mode);
  1448.       }
  1449.       rename(tempFile, pref->filename());
  1450.     }
  1451.     updateFileStat();
  1452.     return True;
  1453.   }
  1454. }
  1455.  
  1456. void
  1457. VCal::loadMenu()
  1458. {
  1459.   char *oldFile;
  1460.  
  1461.   theFileSelectionDialog->setTitle("Load Database");
  1462.   theFileSelectionDialog->setSelection(pref->filename());
  1463.   theFileSelectionDialog->postAndWait(NULL, VkFormat("%s.%s", className(), "loadDialog"), this);
  1464.   
  1465.   if (theFileSelectionDialog->fileName())
  1466.     {
  1467.       oldFile = strdup(pref->filename());
  1468.       pref->setFilename(theFileSelectionDialog->fileName());
  1469.       if (!readFile()) {
  1470.     // If the load fails, don't change the filename for the next save
  1471.     pref->setFilename(oldFile);
  1472.       }
  1473.       updateDisplay(True);
  1474.       free(oldFile);
  1475.     }
  1476. }
  1477.  
  1478. void
  1479. VCal::saveAsMenu()
  1480. {
  1481.   theFileSelectionDialog->setTitle("Save Database");
  1482.   theFileSelectionDialog->setSelection(pref->filename());
  1483.   theFileSelectionDialog->postAndWait(NULL, VkFormat("%s.%s", className(), "saveDialog"), this);
  1484.   
  1485.   if (theFileSelectionDialog->fileName())
  1486.     {
  1487.       pref->setFilename(theFileSelectionDialog->fileName());
  1488.       writeFile();
  1489.       pref->savePreferences();
  1490.     }
  1491. }
  1492.  
  1493. void
  1494. VCal::saveAsTextOK()
  1495. {
  1496.   FILE *fd;
  1497.   char str[MAXSTR];
  1498.  
  1499.   if (theFileSelectionDialog->fileName()) {
  1500.     if (fd = fopen(theFileSelectionDialog->fileName(), "w")) {
  1501.       if (dayText->getValue()) {
  1502.     emitDay(fd, saveDay, saveMonth, saveYear);
  1503.       } else if (weekText->getValue()) {
  1504.     emitWeek(fd, saveDay, saveMonth, saveYear);
  1505.       } else if (monthText->getValue()) {
  1506.     emitMonth(fd, saveDay, saveMonth, saveYear);
  1507.       }
  1508.       fclose(fd);
  1509.     } else {
  1510.       sprintf(str, "Couldn't write to %s", theFileSelectionDialog->fileName());
  1511.       theWarningDialog->post(str);
  1512.     }
  1513.   }
  1514. }
  1515.  
  1516. void
  1517. VCal::saveAsTextCancel()
  1518. {
  1519.   radioText->deleteChildren();
  1520.   delete radioText;
  1521.   radioText = NULL;
  1522.   dayText = weekText = monthText = NULL;
  1523. }
  1524.  
  1525. void
  1526. VCal::displayMonthOverview()
  1527. {
  1528.   theApplication->busy();
  1529.   if (!monthOverview) {
  1530.     monthOverview = new MonthOverview(this, "monthOverview");
  1531.   }
  1532.   monthOverview->selectMonth(nowMonth, nowYear);
  1533.   monthOverview->open();
  1534.   monthOverview->raise();
  1535.   monthOverview->show();
  1536.   theApplication->notBusy();
  1537. }
  1538.  
  1539. void
  1540. VCal::displayWeekOverview(int day, int month, int year)
  1541. {
  1542.   theApplication->busy();
  1543.   if (!weekOverview) {
  1544.     weekOverview = new WeekOverview(this, "weekOverview");
  1545. // Gotta build the grid right away...
  1546.     weekOverview->show();
  1547.   }
  1548.   weekOverview->selectWeek(day, month, year);
  1549.   weekOverview->open();
  1550.   weekOverview->raise();
  1551.   weekOverview->show();
  1552.   theApplication->notBusy();
  1553. }
  1554.  
  1555. void
  1556. VCal::monthPrint()
  1557. {
  1558.   theApplication->busy();
  1559.   getPrint()->printMonthUI(month, year);
  1560.   theApplication->notBusy();
  1561. }
  1562.  
  1563. void
  1564. VCal::prefMenu()
  1565. {
  1566.   if (!prefDialog) {
  1567.     prefDialog = new PrefDialog(this, pref, "prefDialog");
  1568.   } else {
  1569.     prefDialog->updateDisplay();
  1570.   }
  1571.   prefDialog->post(NULL, NULL, NULL, NULL, NULL, "prefDialog");
  1572.   if (prefDialog->baseWidget() &&
  1573.       XtWindow(prefDialog->baseWidget())) {
  1574.     XRaiseWindow(XtDisplay(prefDialog->baseWidget()),
  1575.          XtWindow(prefDialog->baseWidget()));
  1576.   }
  1577. }
  1578.  
  1579. void
  1580. VCal::factoryMenu()
  1581. {
  1582.   if (theQuestionDialog->postAndWait("Are you sure that you want to revert your preferences settings?")) {
  1583.     theApplication->busy();
  1584.     pref->useFactory();
  1585.     if (prefDialog) {
  1586.       prefDialog->updateDisplay();
  1587.     }
  1588.     prefChanged();
  1589.     theApplication->notBusy();
  1590.   }
  1591. }
  1592.  
  1593. void
  1594. VCal::checkAlarms(int t)
  1595. {
  1596.   Entry *entry;
  1597.   RepeatingEntry *rentry;
  1598.   int alarmIndex;
  1599.  
  1600.   if (today) {
  1601.     today->rewind();
  1602.     while (entry = today->nextEntry()) {
  1603.       if (entry->alarmApplies(t, &alarmIndex) &&
  1604.       !entry->alarmFired(alarmIndex)) {
  1605.     handleEntryAlarm(entry, nowDay, nowMonth, nowYear);
  1606.     entry->setAlarmFired(1, alarmIndex);
  1607.       }
  1608.     }
  1609.   }
  1610.   rentry = repeatingEntries;
  1611.   while (rentry->next()) {
  1612.     rentry = rentry->next();
  1613.     if (rentry->alarmApplies(t, &alarmIndex) &&
  1614.     !rentry->alarmFired(alarmIndex) &&
  1615.     rentry->repeatApplies(nowDay, nowMonth, nowYear)) {
  1616.       handleEntryAlarm(rentry, nowDay, nowMonth, nowYear);
  1617.       rentry->setAlarmFired(1, alarmIndex);
  1618.     }
  1619.   }
  1620. }
  1621.  
  1622. void
  1623. VCal::checkDayAdvanceAlarms()
  1624. {
  1625.   MemoryInfo *info;
  1626.   Entry *entry;
  1627.   RepeatingEntry *rentry;
  1628.   int alarmIndex;
  1629.   int matchDay, matchMonth, matchYear;
  1630.  
  1631.   info = dayInfo;
  1632.   while (info->next()) {
  1633.     info = info->next();
  1634.     info->rewind();
  1635.     while (entry = info->nextEntry()) {
  1636.       if (entry->alarmApplies(nowDay, nowMonth, nowYear, &alarmIndex,
  1637.                   &matchDay, &matchMonth, &matchYear)) {
  1638.     handleEntryAlarm(entry, matchDay, matchMonth, matchYear);
  1639.     entry->setAlarmFired(1, alarmIndex);
  1640.       }
  1641.     }
  1642.   }
  1643.   rentry = repeatingEntries;
  1644.   while (rentry->next()) {
  1645.     rentry = rentry->next();
  1646.     if (rentry->alarmApplies(nowDay, nowMonth, nowYear, &alarmIndex,
  1647.                  &matchDay, &matchMonth, &matchYear)) {
  1648. // Hmm.  How do we get the real date information?
  1649.       handleEntryAlarm(rentry, matchDay, matchMonth, matchYear);
  1650.       rentry->setAlarmFired(1, alarmIndex);
  1651.     }
  1652.   }
  1653. }
  1654.  
  1655. void
  1656. VCal::postAlarm(Entry *entry, int day, int month, int year, Boolean late)
  1657. {
  1658.   char *str, dstr[256], tstr[256];
  1659.   int ddiff;
  1660.   VkQuestionDialog *dialog;
  1661.   EntryAlarmInfo *info;
  1662.   
  1663.   XForceScreenSaver(XtDisplay(_baseWidget), ScreenSaverReset);
  1664.   info = entry->alarmInfo();
  1665.   dialog = (VkQuestionDialog *) info->alarm;
  1666.   if (dialog) {
  1667.     dialog->hide();
  1668.   } else {
  1669.     if (entry->repeating()) {
  1670.       dialog = new VkQuestionDialog("repeatAlarmDialog");
  1671.     } else {
  1672.       dialog = new VkQuestionDialog("alarmDialog");
  1673.     }
  1674.     info->alarm = (void *) dialog;
  1675.     info->data = (void *) this;
  1676.     dialog->centerOnScreen(TRUE);
  1677.   }
  1678.   ddiff = computeDayDifference(day, month, year, nowDay, nowMonth, nowYear);
  1679.   if (!ddiff) {
  1680.     strcpy(dstr, "");
  1681.   } else if (ddiff == 1) {
  1682.     strcpy(dstr, "Tomorrow, ");
  1683.   } else {
  1684.     formatDate(day, month, year, dstr);
  1685.     strcat(dstr, ", ");
  1686.   }
  1687.   formatTime(entry->start() / 60, entry->start() % 60, pref->clock24(), tstr);
  1688.   str = (char *) malloc(strlen(entry->text())+256);
  1689.   if (late) {
  1690.     sprintf(str, "VCal (missed): %s%s\n\n%s", dstr, tstr, entry->text());
  1691.   } else {
  1692.     sprintf(str, "VCal: %s%s\n\n%s", dstr, tstr, entry->text());
  1693.   }
  1694.   dialog->post(str,
  1695.            VCal::alarm_dismiss,
  1696.            VCal::alarm_delete,
  1697.            VCal::alarm_snooze,
  1698.            (XtPointer) info);
  1699.   free(str);
  1700. }
  1701.  
  1702. void
  1703. VCal::clearData()
  1704. {
  1705.   // Memory leak -- delete lists
  1706.   delete repeatingEntries;
  1707.   delete dayInfo;
  1708.   
  1709.   dayInfo = new MemoryInfo();
  1710.   repeatingEntries = new RepeatingEntry();
  1711.   numDays = 0;
  1712.   numRepeats = 0;
  1713. }
  1714.  
  1715. void
  1716. VCal::selectMonth(int month)
  1717. {
  1718.   displayingCurrent = (month == nowMonth && year == nowYear);
  1719.   setDisplayDate(nowDay, month, year);
  1720. }
  1721.  
  1722. Boolean VCal::okToQuit()
  1723. {
  1724.   if (dirty)
  1725.     {
  1726.       VkDialogManager::VkDialogReason result;
  1727.       
  1728.       result = theQuestionDialog->postAndWait("There are unsaved changes.  Save before quitting?",
  1729.                           TRUE, TRUE, TRUE, 
  1730.                           "saveBeforeQuit", 
  1731.                           this);
  1732.       if(result == VkDialogManager::OK)
  1733.     {
  1734.       writeFile();
  1735.       return TRUE;
  1736.     }
  1737.       else if(result == VkDialogManager::CANCEL)
  1738.     {
  1739.       return FALSE;
  1740.     }
  1741.     }
  1742.   return TRUE;
  1743. }
  1744.  
  1745.  
  1746. void
  1747. VCal::resizeDay()
  1748. {
  1749.   Dimension width, height, marginW, marginH, spacing, bWidth, bHeight, n;
  1750.   short numColumns;
  1751.   int each;
  1752.   
  1753.   count = 0;
  1754.   XtSetArg(args[count], XmNwidth, &width);  count++;
  1755.   XtSetArg(args[count], XmNheight, &height);  count++;
  1756.   XtSetArg(args[count], XmNmarginWidth, &marginW);  count++;
  1757.   XtSetArg(args[count], XmNmarginHeight, &marginH);  count++;
  1758.   XtSetArg(args[count], XmNspacing, &spacing);  count++;
  1759.   XtSetArg(args[count], XmNnumColumns, &numColumns);  count++;
  1760.   XtGetValues(dayArea, args, count);
  1761.   count = 0;
  1762.   XtSetArg(args[count], XmNwidth, &bWidth);  count++;
  1763.   XtSetArg(args[count], XmNheight, &bHeight);  count++;
  1764.   XtGetValues(dayWidgets[0], args, count);
  1765.   count = 0;
  1766.   if (width >= 2*marginW+(DAYS_IN_WEEK-1)*spacing) {
  1767.     n = (width-2*marginW-(DAYS_IN_WEEK-1)*spacing)/DAYS_IN_WEEK-2*buttonBorder;
  1768.     if (n != bWidth) {
  1769.       XtSetArg(args[count], XmNwidth, n);  count++;
  1770.     }
  1771.   }
  1772.   if (height >= 2*marginH+(numColumns-1)*spacing) {
  1773.     n = (height-2*marginH-(numColumns-1)*spacing)/numColumns-2*buttonBorder;
  1774.     if (n != bHeight) {
  1775.       XtSetArg(args[count], XmNheight, n);  count++;
  1776.     }
  1777.   }
  1778.   if (count) {
  1779.     // If I don't do this, resizing the children has no effect
  1780.     XtUnmanageChild(dayArea);
  1781.     // If I don't do this, the buttons can't get smaller
  1782.     XtUnmanageChildren(dayHeaders, DAYS_IN_WEEK);
  1783.     XtUnmanageChildren(dayWidgets, MAX_DAYS);
  1784.     for (each=0; each<DAYS_IN_WEEK; each++) {
  1785.       XtSetValues(dayHeaders[each], args, count);
  1786.     }
  1787.     for (each=0; each<MAX_DAYS; each++) {
  1788.       XtSetValues(dayWidgets[each], args, count);
  1789.     }
  1790.     XtManageChildren(dayHeaders, DAYS_IN_WEEK);
  1791.     XtManageChildren(dayWidgets, MAX_DAYS);
  1792.     XtManageChild(dayArea);
  1793.   }
  1794.   if (hidden) {
  1795.     XtDestroyWidget(hidden);
  1796.     hidden = NULL;
  1797.   }
  1798. }
  1799.  
  1800. void
  1801. VCal::displayActionsPopup(XButtonPressedEvent *event)
  1802. {
  1803.   XmMenuPosition(actionsPopup->baseWidget(), event);
  1804.   XtManageChild(actionsPopup->baseWidget());
  1805. }
  1806.  
  1807. void
  1808. VCal::updateFileStat()
  1809. {
  1810.   if (stat(pref->filename(), &lastFileStat) == -1) {
  1811.     lastFileStat.st_mtime = 0;
  1812.   }
  1813. }
  1814.  
  1815. void
  1816. VCal::checkFileStat()
  1817. {
  1818.   if (!checkInhibited) {
  1819.     if (fileChanged()) {
  1820.       if (!dirty) {
  1821.     updateDisplay(True);
  1822.       } else {
  1823.     inhibitCheckFile();
  1824.     if (theQuestionDialog->postAndWait(
  1825.                        "Someone else has changed the database file and you have unsaved changes.\nThrow away your changes?")) {
  1826.       updateDisplay(True);
  1827.     } else {
  1828.       // The next time we write, user will be asked to overwrite the other changes
  1829.     }
  1830.     allowCheckFile();
  1831.       }
  1832.     }
  1833.   }
  1834. }
  1835.  
  1836. Boolean
  1837. VCal::fileChanged()
  1838. {
  1839.   struct stat currentFileStat;
  1840.   
  1841.   if (stat(pref->filename(), ¤tFileStat) == -1) {
  1842.     return False;
  1843.   } else {
  1844.     return (!currentFileStat.st_mtime ||
  1845.         currentFileStat.st_mtime != lastFileStat.st_mtime);
  1846.   }
  1847. }
  1848.  
  1849. void
  1850. VCal::checkFileSoon()
  1851. {
  1852.   XtAppAddWorkProc(theApplication->appContext(),
  1853.            VCal::check_file, (XtPointer) this);
  1854. }
  1855.  
  1856. void
  1857. VCal::markTodayFired(struct tm *now, Boolean lates)
  1858. {
  1859.   Entry *entry;
  1860.   RepeatingEntry *rentry;
  1861.  
  1862.   if (today) {
  1863.     today->rewind();
  1864.     while (entry = today->nextEntry()) {
  1865.       if (entry->start() < now->tm_hour*60+now->tm_min) {
  1866.     if (lates) {
  1867.       handleEntryAlarm(entry, nowDay, nowMonth, nowYear, True);
  1868.     }
  1869.     entry->setAlarmFired(1, ALL_NOTIFICATIONS);
  1870.       } else {
  1871.     entry->setAlarmFired(0, ALL_NOTIFICATIONS);
  1872.       }
  1873.     }
  1874.   }
  1875.   rentry = repeatingEntries;
  1876.   while (rentry->next()) {
  1877.     rentry = rentry->next();
  1878.     if (rentry->start() < now->tm_hour*60+now->tm_min &&
  1879.     rentry->repeatApplies(nowDay, nowMonth, nowYear)) {
  1880.       if (lates) {
  1881.     handleEntryAlarm(rentry, nowDay, nowMonth, nowYear, True);
  1882.       }
  1883.       rentry->setAlarmFired(1, ALL_NOTIFICATIONS);
  1884.     } else {
  1885.       rentry->setAlarmFired(0, ALL_NOTIFICATIONS);
  1886.     }
  1887.   }
  1888. }
  1889.  
  1890. void
  1891. VCal::snoozeAlarm(Entry *entry)
  1892. {
  1893.   XtIntervalId id;
  1894.   
  1895.   id = XtAppAddTimeOut(theApplication->appContext(),
  1896.                pref->snoozeMinutes()*60*1000,
  1897.                (XtTimerCallbackProc) VCal::snooze_proc,
  1898.                (XtPointer) this);
  1899.   entry->setSnoozeData((void *) id);
  1900. }
  1901.  
  1902. void
  1903. VCal::handleSnooze(XtIntervalId id)
  1904. {
  1905.   Entry *entry;
  1906.   RepeatingEntry *rentry;
  1907.   int oldDay, oldMonth, oldYear;
  1908.   
  1909.   if (today) {
  1910.     today->rewind();
  1911.     while (entry = today->nextEntry()) {
  1912.       if (((XtIntervalId) entry->snoozeData()) == id) {
  1913.     entry->setSnoozeData(NULL);
  1914.     handleEntryAlarm(entry, nowDay, nowMonth, nowYear, False);
  1915.       }
  1916.     }
  1917.   }
  1918.   rentry = repeatingEntries;
  1919.   while (rentry->next()) {
  1920.     rentry = rentry->next();
  1921.     if (((XtIntervalId) rentry->snoozeData()) == id) {
  1922.       rentry->setSnoozeData(NULL);
  1923. // Actual repeat match information is no longer available
  1924.       handleEntryAlarm(rentry, nowDay, nowMonth, nowYear, False);
  1925.     }
  1926.   }
  1927.   
  1928.   // Oh well, I guess we past Midnight while we snoozed, go back to yesterday
  1929.   
  1930.   if (nowDay > 1) {
  1931.     oldDay = nowDay-1;
  1932.     oldMonth = nowMonth;
  1933.     oldYear = nowYear;
  1934.   } else if (nowMonth > 1) {
  1935.     oldDay = NumberOfDays(nowMonth-1, nowYear);
  1936.     oldMonth = nowMonth-1;
  1937.     oldYear = nowYear;
  1938.   } else {
  1939.     oldDay = NumberOfDays(12, nowYear);
  1940.     oldMonth = 12;
  1941.     oldYear = nowYear-1;
  1942.   }
  1943.   today = findDateInfo(oldDay, oldMonth, oldYear);
  1944.   if (today) {
  1945.     today->rewind();
  1946.     while (entry = today->nextEntry()) {
  1947.       if (((XtIntervalId) entry->snoozeData()) == id) {
  1948.     entry->setSnoozeData(NULL);
  1949.     handleEntryAlarm(entry, oldDay, oldMonth, oldYear, False);
  1950.       }
  1951.     }
  1952.   }
  1953.   today = findDateInfo(nowDay, nowMonth, nowYear);
  1954. }
  1955.  
  1956. void
  1957. VCal::alarmDismiss(EntryAlarmInfo *info)
  1958. {
  1959.   VkQuestionDialog *dialog;
  1960.   
  1961.   if (dialog = (VkQuestionDialog *) info->alarm) {
  1962.     scheduleDialogDelete(dialog);
  1963.     info->alarm = NULL;
  1964.   }
  1965. }
  1966.  
  1967. void
  1968. VCal::alarmDelete(EntryAlarmInfo *info)
  1969. {
  1970.   Boolean worked = False;
  1971.   
  1972.   alarmDismiss(info);
  1973.   if (!pref->notifyConfirmDelete() ||
  1974.       theQuestionDialog->postAndWait("Are you sure that you want to delete the entry that caused this alarm?")) {
  1975.     if (dayView) {
  1976.       if (dayView->eraseEntry(info->entry)) {
  1977.     worked = True;
  1978.       }
  1979.     }
  1980.     if (info->entry->repeating()) {
  1981.       removeRepeatingEntry((RepeatingEntry *) info->entry);
  1982.       worked = True;
  1983.     } else if (today && today->removeEntry(info->entry)) {
  1984.       worked = True;
  1985.     }
  1986.     if (worked) {
  1987.       if (pref->saveChangesAutomatically()) {
  1988.     writeFile();
  1989.       }
  1990.       updateDisplay(False);
  1991.     } else {
  1992.       theWarningDialog->post("That entry could not be deleted.  To delete entries from other days,\nuse the DayView and access the entry directly.");
  1993.     }
  1994.   }
  1995. }
  1996.  
  1997. void
  1998. VCal::alarmSnooze(EntryAlarmInfo *info)
  1999. {
  2000.   alarmDismiss(info);
  2001.   
  2002.   snoozeAlarm(info->entry);
  2003. }
  2004.  
  2005. void
  2006. VCal::scheduleDialogDelete(VkQuestionDialog *dialog)
  2007. {
  2008.   XtAppAddWorkProc(theApplication->appContext(),
  2009.            VCal::dialog_delete, (XtPointer) dialog);
  2010. }
  2011.  
  2012. void
  2013. VCal::handleEntryAlarm(Entry *entry, int day, int month, int year,
  2014.                Boolean late)
  2015. {
  2016.   if (pref->alarms()) {
  2017.     if (entry->notifyMail()) {
  2018.       sendMailNotification(entry, day, month, year, late);
  2019.     }
  2020.     if (strlen(entry->notifyCommand())) {
  2021.       VkExecCommand(entry->notifyCommand());
  2022.     }
  2023.     if (entry->notifyPopup()) {
  2024.       postAlarm(entry, day, month, year, late);
  2025.     }
  2026.     if (entry->notifyBell()) {
  2027.       XBell(theApplication->display(), 0);
  2028.     }
  2029.   }
  2030. }
  2031.  
  2032. void
  2033. VCal::sendMailNotification(Entry *entry, int day, int month, int year,
  2034.                Boolean late)
  2035. {
  2036.   char str[MAXSTR], tempFile[256], dstr[256], tstr[256];
  2037.   int ddiff;
  2038.   struct passwd *passwd;
  2039.   FILE *fd;
  2040.   
  2041.   if (getenv("TMPDIR")) {
  2042.     sprintf(tempFile, "%s/.vcal-XXXXXX", getenv("TMPDIR"));
  2043.   } else {
  2044.     sprintf(tempFile, "/tmp/.vcal-XXXXXX");
  2045.   }
  2046.   mktemp(tempFile);
  2047.   if (fd = fopen(tempFile, "w")) {
  2048.     ddiff = computeDayDifference(day, month, year, nowDay, nowMonth, nowYear);
  2049.     if (!ddiff) {
  2050.       strcpy(dstr, "");
  2051.     } else if (ddiff == 1) {
  2052.       strcpy(dstr, "Tomorrow, ");
  2053.     } else {
  2054.       formatDate(day, month, year, dstr);
  2055.       strcat(dstr, ", ");
  2056.     }
  2057.     formatTime(entry->start() / 60, entry->start() % 60, pref->clock24(),
  2058.            tstr);
  2059.     if (late) {
  2060.       fprintf(fd, "VCal (missed): %s%s\n\n", dstr, tstr);
  2061.     } else {
  2062.       fprintf(fd, "VCal: %s%s\n\n", dstr, tstr);
  2063.     }
  2064.     fputs(entry->text(), fd);
  2065.     fclose(fd);
  2066.     if (passwd = getpwuid(getuid())) {
  2067.       sprintf(str, "/usr/sbin/Mail -s 'VCal-Notification' %s < %s",
  2068.           passwd->pw_name, tempFile);
  2069.       system(str);
  2070.     }
  2071.     unlink(tempFile);
  2072.   }
  2073. }
  2074.  
  2075. Boolean
  2076. VCal::emitSingleDay(FILE *fd, int day, int month, int year,
  2077.             Boolean headerAlways, Boolean formatYear, char *prefix)
  2078. {
  2079.   MemoryInfo *dayInfo;
  2080.   char str[MAXSTR];
  2081.   Entry *entry;
  2082.   RepeatingEntry *rentry;
  2083.   int weekday;
  2084.  
  2085.   dayInfo = findDateInfo(day, month, year);
  2086.   initCollect();
  2087.   if (dayInfo) {
  2088.     dayInfo->rewind();
  2089.     while (entry = dayInfo->nextEntry()) {
  2090.       collectSingleEntry(entry);
  2091.     }
  2092.   }
  2093.   rentry = repeatingEntries;
  2094.   while (rentry->next()) {
  2095.     rentry = rentry->next();
  2096.     if (rentry->repeatApplies(day, month, year)) {
  2097.       collectSingleEntry(rentry);
  2098.     }
  2099.   }
  2100.   if (collectedCount() || headerAlways) {
  2101.     weekday = computeWeekday(day, month, year);
  2102.     formatDate(weekday, day, month, formatYear ? year : 0, str);
  2103.     if (prefix) {
  2104.       fputs(prefix, fd);
  2105.     }
  2106.     fprintf(fd, "%s\n\n", str);
  2107.     emitCollectedEntries(fd);
  2108.   }
  2109.   return (collectedCount() > 0);
  2110. }
  2111.  
  2112. void
  2113. VCal::emitCollectedEntries(FILE *fd)
  2114. {
  2115.   int each;
  2116.  
  2117.   if (numCollect) {
  2118.     sortCollection();
  2119.     for (each=0; each<numCollect; each++) {
  2120.       entries[each]->printEntry(fd, pref->clock24());
  2121.     }
  2122.   }
  2123. }
  2124.  
  2125. void
  2126. VCal::changeDayView(int day, int month, int year)
  2127. {
  2128.   MemoryInfo *info;
  2129.  
  2130.   info = dayInfo;
  2131.   while (info->next() &&
  2132.      !info->next()->sameDate(day, month, year)) {
  2133.     info = info->next();
  2134.   }
  2135.   if (!info->next()) {
  2136.     numDays++;
  2137.     info->setNext(new MemoryInfo());
  2138.     info->next()->setDate(day, month, year);
  2139.   }
  2140.   info = info->next();
  2141.   dayView->selectDate(day, month, year, info, repeatingEntries);
  2142. }
  2143.  
  2144. /**********************************************************************/
  2145.  
  2146. VCal::VCal(const char *docName): VkWindow(docName)
  2147. {
  2148.   monthHeader = NULL;
  2149.   month = year = nowMonth = nowYear = nowDay = 0;
  2150.   displayingCurrent = True;
  2151.   dayView = NULL;
  2152.   dayInfo = new MemoryInfo();
  2153.   numDays = 0;
  2154.   numRepeats = 0;
  2155.   dirty = False;
  2156.   today = NULL;
  2157.   repeatingEntries = new RepeatingEntry();
  2158.   monthPrev = NULL;
  2159.   actionsPopup = NULL;
  2160.   lastFileStat.st_mtime = 0;
  2161.   checkInhibited = False;
  2162.   hidden = NULL;
  2163.   updating = False;
  2164.   pref = NULL;
  2165.   prefDialog = NULL;
  2166.   iconIsTime = False;
  2167.   iconIsDate = False;
  2168.   radioText = NULL;
  2169.   dayText = weekText = monthText = NULL;
  2170.   entries = NULL;
  2171.   monthOverview = NULL;
  2172.   weekOverview = NULL;
  2173.   tile = NULL;
  2174.   print = NULL;
  2175.   writeID = NULL;
  2176.   
  2177.   VkInitializeChildrenHandler();
  2178. }
  2179.  
  2180. VCal::~VCal()
  2181. {
  2182.   if (actionsPopup) {
  2183.     delete actionsPopup;
  2184.   }
  2185.   if (monthPrev) {
  2186.     delete monthPrev;
  2187.     delete monthNext;
  2188.   }
  2189.   if (pref) {
  2190.     delete pref;
  2191.   }
  2192.   if (prefDialog) {
  2193.     delete prefDialog;
  2194.   }
  2195.   if (print) {
  2196.     delete print;
  2197.   }
  2198.   if (monthHeader) {
  2199.     XtRemoveEventHandler(monthHeader, ButtonPressMask, False,
  2200.              (XtEventHandler) VCal::month_select,
  2201.              (XtPointer) this);
  2202.     XtRemoveEventHandler(dayArea, StructureNotifyMask, False,
  2203.              (XtEventHandler) VCal::resize_day,
  2204.              (XtPointer) this);
  2205.   }
  2206.   if (entries) {
  2207.     free(entries);
  2208.   }
  2209.   if (tile) {
  2210.     XFreePixmap(XtDisplay(_baseWidget), tile);
  2211.   }
  2212.   
  2213.   if (dayView) {
  2214.     //    delete dayView;  Can\'t delete this, it's already been done by theApplication
  2215.   }
  2216.   if (monthOverview) {
  2217.     //    delete monthOverview;  Can\'t delete this, it's already been done by theApplication
  2218.   }
  2219.   if (weekOverview) {
  2220.     //    delete weekOverview;  Can\'t delete this, it's already been done by theApplication
  2221.   }
  2222.   
  2223.   clearData();
  2224. }
  2225.  
  2226. const char *
  2227. VCal::className()
  2228. {
  2229.   return "VCal";
  2230. }
  2231.